/**
 * @file order_gateway.h
 * @brief 订单网关类的定义
 * 
 * @details 该文件定义了订单网关类，负责处理交易系统与交易所之间的订单通信。
 * 订单网关类实现了将交易引擎生成的客户端请求发送给交易所，
 * 并将交易所返回的客户端响应传递给交易引擎的功能。
 * 
 * @author 原作者
 * @date 2023
 */

#pragma once

#include <functional>

#include "common/thread_utils.h"
#include "common/macros.h"
#include "common/tcp_server.h"

#include "exchange/order_server/client_request.h"
#include "exchange/order_server/client_response.h"

namespace Trading {
  /**
   * @brief 订单网关类
   * 
   * @details 该类负责处理交易系统与交易所之间的订单通信。
   * 它通过TCP连接与交易所的订单服务器通信，将交易引擎生成的客户端请求
   * 发送给交易所，并将交易所返回的客户端响应传递给交易引擎。
   * 该类维护订单的序列号，确保订单的可靠传输和正确处理。
   */
  class OrderGateway {
  public:
    /**
     * @brief 构造函数
     * 
     * @details 初始化订单网关对象，设置客户端 ID、请求和响应队列以及网络参数。
     * 
     * @param client_id 客户端 ID，用于标识当前交易系统实例
     * @param client_requests 客户端请求的无锁队列指针，用于从交易引擎获取请求并发送给交易所
     * @param client_responses 客户端响应的无锁队列指针，用于将交易所的响应传递给交易引擎
     * @param ip 交易所订单服务器的 IP 地址
     * @param iface 网络接口名称
     * @param port 交易所订单服务器的端口号
     */
    OrderGateway(ClientId client_id,
                 Exchange::ClientRequestLFQueue *client_requests,
                 Exchange::ClientResponseLFQueue *client_responses,
                 std::string ip, const std::string &iface, int port);

    /**
     * @brief 析构函数
     * 
     * @details 停止订单网关的运行并等待5秒，确保所有资源被正确释放
     */
    ~OrderGateway() {
      stop();

      using namespace std::literals::chrono_literals;
      std::this_thread::sleep_for(5s);
    }

    /**
     * @brief 启动订单网关
     * 
     * @details 连接到交易所的订单服务器并启动订单网关的主线程。
     * 该方法会设置运行标志，连接到指定的TCP服务器，并创建一个新线程来执行run方法。
     * 
     * @return void 无返回值
     * 
     * @throw 如果无法连接到交易所或无法启动线程，将通过ASSERT宏抛出异常
     */
    auto start() {
      run_ = true;
      ASSERT(tcp_socket_.connect(ip_, iface_, port_, false) >= 0,
             "Unable to connect to ip:" + ip_ + " port:" + std::to_string(port_) + " on iface:" + iface_ + " error:" + std::string(std::strerror(errno)));
      ASSERT(Common::createAndStartThread(-1, "Trading/OrderGateway", [this]() { run(); }) != nullptr, "Failed to start OrderGateway thread.");
    }

    /**
     * @brief 停止订单网关
     * 
     * @details 通过设置运行标志为false来停止订单网关的主线程
     * 
     * @return void 无返回值
     */
    auto stop() -> void {
      run_ = false;
    }

    /**
     * @brief 已删除的构造函数和赋值运算符
     * 
     * @details 为了防止意外复制或移动订单网关对象，禁用了默认构造函数、复制构造函数、
     * 移动构造函数以及复制赋值和移动赋值运算符
     */
    OrderGateway() = delete;

    OrderGateway(const OrderGateway &) = delete;

    OrderGateway(const OrderGateway &&) = delete;

    OrderGateway &operator=(const OrderGateway &) = delete;

    OrderGateway &operator=(const OrderGateway &&) = delete;

  private:
    /**
     * @brief 客户端 ID
     * 
     * @details 用于标识当前交易系统实例的唯一标识符
     */
    const ClientId client_id_;

    /**
     * @brief 交易所订单服务器的 IP 地址
     */
    std::string ip_;
    
    /**
     * @brief 网络接口名称
     */
    const std::string iface_;
    
    /**
     * @brief 交易所订单服务器的端口号
     */
    const int port_ = 0;

    /**
     * @brief 客户端请求的无锁队列
     * 
     * @details 用于从交易引擎消费客户端请求，并将其转发给交易所的订单服务器
     */
    Exchange::ClientRequestLFQueue *outgoing_requests_ = nullptr;

    /**
     * @brief 客户端响应的无锁队列
     * 
     * @details 用于写入从交易所读取并处理的客户端响应，这些响应将被交易引擎消费
     */
    Exchange::ClientResponseLFQueue *incoming_responses_ = nullptr;

    /**
     * @brief 运行标志
     * 
     * @details 控制主线程循环是否继续运行的标志变量
     */
    volatile bool run_ = false;

    /**
     * @brief 时间字符串缓存
     * 
     * @details 用于日志记录的时间字符串缓存，避免重复创建时间字符串
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器
     * 
     * @details 用于记录订单网关的日志信息
     */
    Logger logger_;

    /**
     * @brief 下一个外发序列号
     * 
     * @details 用于跟踪要设置在外发客户端请求上的序列号
     */
    size_t next_outgoing_seq_num_ = 1;
    
    /**
     * @brief 下一个期望的序列号
     * 
     * @details 用于跟踪在传入客户端响应上期望的序列号
     */
    size_t next_exp_seq_num_ = 1;

    /**
     * @brief 与交易所订单服务器的 TCP 连接
     * 
     * @details 用于与交易所的订单服务器进行通信的 TCP 套接字
     */
    Common::TCPSocket tcp_socket_;

  private:
    /**
     * @brief 主线程循环函数
     * 
     * @details 该函数实现了订单网关的主要工作循环，负责从客户端请求队列中获取请求，
     * 将其发送给交易所，并接收交易所返回的响应。该函数维护订单的序列号，
     * 确保订单的可靠传输和正确处理。
     * 
     * @return void 无返回值
     * 
     * @note 该函数不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto run() noexcept -> void;

    /**
     * @brief 接收回调函数
     * 
     * @details 当从交易所接收到客户端响应时调用该回调函数。函数会执行一系列检查，
     * 包括验证客户端ID和序列号的正确性，然后将响应转发到与交易引擎连接的无锁队列中。
     * 
     * @param socket TCP套接字指针，包含接收到的数据
     * @param rx_time 接收时间戳（纳秒）
     * @return void 无返回值
     * 
     * @note 该函数不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto recvCallback(TCPSocket *socket, Nanos rx_time) noexcept -> void;
  };
}
